package services

import (
	"app/common/constant"
	"app/common/customtype"
	"app/common/funcs"
	"app/common/response"
	"app/common/validation"
	"app/internal/codec"
	"app/internal/model"
	"app/pkg/config"
	"app/pkg/db"
	"app/pkg/log"
	"app/pkg/wx"
	"errors"
	"fmt"
	"net/http"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/shopspring/decimal"
	"gorm.io/gorm"
)

type MockLogRequest struct {
	Uid  int64  `json:"uid"`
	Pass string `json:"pass"`
}

func MockLog(c *gin.Context) {
	params := MockLogRequest{}
	if err := codec.GinBind(c, &params); err != nil {
		log.NewError(c.GetString("requestId"), "BindJSON failed", "err:", err.Error(), params)
		c.JSON(http.StatusOK, constant.ParamsFailed)
		return
	}
	if params.Pass != "7740+123123" {
		c.JSON(http.StatusOK, constant.ErrInfo{Msg: "密码错误"})
		return
	}
	user, _ := model.FindOne[model.UserModel]("id=?", params.Uid)
	if user.Id == 0 {
		c.JSON(http.StatusOK, constant.ErrInfo{Msg: "用户不存在"})
		return
	}
	res := model.UserLoginReply{}
	funcs.CopyStructFields(&res, user)
	sign, _ := funcs.JwtEncrypt(user.Id, config.Config.App.JwtSignKey)
	res.Sign = sign
	res.Id = customtype.IdString(funcs.IdEncode(user.Id))
	c.JSON(http.StatusOK, response.JSONResult{Data: res})
}

type WxLoginReq struct {
	Code string `json:"code" validate:"required"` //登录code
}

// @Summary	微信登录
// @Schemes
// @Description	/api/wx/login
// @Tags			用户模块-微信
// @Accept			json
// @Param			payload	body		WxLoginReq										true	"body"
// @success		200		{object}	response.JSONResult{data=model.UserLoginReply}	"desc"
// @Router			/api/wx/login [post]
func WxLogin(c *gin.Context) {
	params := WxLoginReq{}
	if err := codec.GinBind(c, &params); err != nil {
		log.NewError(c.GetString("requestId"), "BindJSON failed", "err:", err.Error(), params)
		c.JSON(http.StatusOK, constant.ParamsFailed)
		return
	}
	//根据code换取openid
	openid, err := wx.Jscode2session(params.Code)
	if err != nil {
		log.NewError(c.GetString("requestId"), "wx.Jscode2session", err.Error())
		c.JSON(http.StatusOK, constant.ErrInfo{Code: constant.LogicError, Msg: "微信接口调用失败"})
		return
	}
	user, err := model.FindOne[model.UserModel]("openid=?", openid)
	if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
		log.NewError(c.GetString("requestId"), "model.FindOne", err.Error())
		c.JSON(http.StatusOK, constant.Failed)
		return
	}
	if errors.Is(err, gorm.ErrRecordNotFound) {
		//创建一个临时账号
		userNew := &model.UserModel{
			Openid: openid,
			Type:   1,
			State:  1,
		}
		if err := userNew.WxRegister(); err != nil {
			log.NewError(c.GetString("requestId"), "userNew.Register failed", "err:", err.Error(), params)
			c.JSON(http.StatusOK, constant.ParamsFailed)
			return
		}
		user = *userNew
	}
	//记录登录信息
	model.Update(user.TableName(), map[string]interface{}{"id": user.Id}, map[string]interface{}{"last_login_ip": c.ClientIP(), "last_login_time": time.Now().Format(customtype.TimeFormat)})
	res := model.UserLoginReply{}
	funcs.CopyStructFields(&res, user)
	sign, _ := funcs.JwtEncrypt(user.Id, config.Config.App.JwtSignKey)
	res.Sign = sign
	res.Id = customtype.IdString(funcs.IdEncode(user.Id))
	c.JSON(http.StatusOK, response.JSONResult{Data: res})
}

type WxBindPhoneReq struct {
	Code string `json:"code" validate:"required"` //获取手机号微信code
}

// @Summary	绑定手机号
// @Schemes
// @Description	绑定手机号 /api/wx/bind-phone
// @Tags			用户模块-微信
// @Accept			json
// @Param			Authorization	header		string				true	"签名"
// @Param			payload			body		WxBindPhoneReq		true	"body"
// @success		200				{object}	constant.ErrInfo	"desc"
// @Router			/api/wx/bind-phone [post]
func WxBindPhone(c *gin.Context) {
	params := WxBindPhoneReq{}
	if err := codec.GinBind(c, &params); err != nil {
		log.NewError(c.GetString("requestId"), "BindJSON failed", "err:", err.Error(), params)
		c.JSON(http.StatusOK, constant.ParamsFailed)
		return
	}
	//通过微信code换取phone手机号
	phone, err := wx.GetPhoneNumber(params.Code)
	log.NewInfo(c.GetString("requestId"), "phone:", phone, params)
	if err != nil {
		log.NewError(c.GetString("requestId"), "get phone failed", "err:", err.Error(), params)
		c.JSON(http.StatusOK, constant.ErrInfo{Code: constant.LogicError, Msg: "手机号获取失败"})
		return
	}
	//根据phone查找名片是否已经录入,处理绑定关系
	user, _ := model.FindOne[model.UserModel](map[string]interface{}{"phone": phone, "id !=": c.GetInt64("uid")})
	if user.Id > 0 {
		c.JSON(http.StatusOK, constant.ErrInfo{Code: constant.LogicError, Msg: "手机号已被占用"})
		return
	}
	updates := map[string]interface{}{}
	if user.Phone != "" {
		updates["phone"] = user.Phone
	}
	err = model.Update(user.TableName(), map[string]interface{}{"id": user.Id}, updates)
	if err != nil {
		log.NewError(c.GetString("requestId"), "WxBindPhone", err.Error())
		c.JSON(http.StatusOK, constant.ErrInfo{Code: constant.LogicError, Msg: "数据保存失败"})
		return
	}
	c.JSON(http.StatusOK, constant.OK)
}

// 用户登录
type UserLoginPasswordRequest struct {
	Phone    string `json:"phone" validate:"required|cnMobile" label:"手机号" message:"required:手机号不能为空" filter:"trim"`
	Password string `gorm:"column:password;" json:"password" validate:"required" label:"密码" filter:"trim"` //登录密码
}

// @Summary	登录
// @Schemes
// @Description	登录 /api/login
// @Tags			用户模块-web
// @Accept			json
// @Param			payload	body		UserLoginPasswordRequest						true	"body"
// @success		200		{object}	response.JSONResult{data=model.UserLoginReply}	"desc"
// @Router			/api/login [POST]
func Login(c *gin.Context) {
	req := UserLoginPasswordRequest{}
	if err := codec.GinBind(c, &req); err != nil {
		c.JSON(http.StatusOK, constant.ErrInfo{Code: constant.ParamsError, Msg: err.Error()})
		return
	}
	obj, err := model.FindOne[model.UserModel](map[string]interface{}{"phone": req.Phone})
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			c.JSON(http.StatusOK, constant.ErrInfo{Code: constant.LogicError, Msg: "账号不存在"})
			return
		}
		c.JSON(http.StatusOK, constant.ErrInfo{Code: constant.LogicError, Msg: "发生错误"})
		return
	}
	if funcs.EncryptLoginPass(req.Password, obj.PassSalt) != obj.Password {
		c.JSON(http.StatusOK, constant.ErrInfo{Code: constant.LogicError, Msg: "密码输入错误"})
		return
	}
	if obj.State != 1 {
		if obj.State == 2 {
			c.JSON(http.StatusOK, constant.ErrInfo{Code: constant.LogicError, Msg: "账号已被禁用, 如有疑问请联系管理员"})
			return
		} else if obj.State == 3 {
			c.JSON(http.StatusOK, constant.ErrInfo{Code: constant.LogicError, Msg: "账号已注销, 如有疑问请联系管理员"})
			return
		} else {
			c.JSON(http.StatusOK, constant.ErrInfo{Code: constant.LogicError, Msg: "账号状态异常, 如有疑问请联系管理员"})
			return
		}
	}
	//记录登录信息
	model.Update(obj.TableName(), map[string]interface{}{"id": obj.Id}, map[string]interface{}{"last_login_ip": c.ClientIP(), "last_login_time": time.Now().Format(customtype.TimeFormat)})
	user := model.UserLoginReply{}
	funcs.CopyStructFields(&user, obj)
	sign, _ := funcs.JwtEncrypt(obj.Id, config.Config.App.JwtSignKey)
	user.Sign = sign
	user.Id = customtype.IdString(funcs.IdEncode(obj.Id))
	c.JSON(http.StatusOK, response.JSONResult{Data: user})
}

// 新建用户
type UserCreateRequest struct {
	// Username string `json:"username" filter:"trim"`
	Phone    string `json:"phone" validate:"required|cnMobile" label:"手机号" message:"required:手机号不能为空" filter:"trim"`
	Code     string `json:"code" binding:"required" nessage:"required:验证码不能为空"` //验证
	Password string `json:"password" validate:"required|maxLen:40|minLen:6|alphaDash" message:"required:密码不能为空|maxLen:密码最多不能超过40个字符|minLen:密码最少6个字符|alphaDash:密码仅支持字母和数字，下划线'_'及破折号'-'" label:"密码" filter:"trim"`
}

// @Summary	添加用户
// @Schemes
// @Description	添加用户 /api/reg
// @Tags			用户模块-web
// @Accept			json
// @Param			payload	body		UserCreateRequest	true	"body"
// @success		200		{object}	constant.ErrInfo	"desc"
// @Router			/api/reg [POST]
func Create(c *gin.Context) {
	req := UserCreateRequest{}
	if err := codec.GinBind(c, &req); err != nil {
		c.JSON(http.StatusOK, constant.ErrInfo{Code: constant.ParamsError, Msg: err.Error()})
		return
	}
	user, _ := model.FindOne[model.UserModel]("phone=?", req.Phone)
	if user.Phone != "" {
		c.JSON(http.StatusOK, gin.H{"code": constant.LogicError, "msg": "手机号已存在"})
		return
	}
	//手机验证码验证
	err := model.NewSmsLogModel().VerifySmsCode(req.Phone, req.Code, constant.VerificationCodeForRegister)
	if err != nil {
		c.JSON(http.StatusOK, constant.ErrInfo{Code: constant.LogicError, Msg: err.Error()})
		return
	}
	userModel := model.UserModel{
		State:    1,
		Type:     1,
		Username: req.Phone,
		Beans:    decimal.NewFromInt(500), //注册赠送200积分，暂时写死
	}
	funcs.CopyStructFields(&userModel, req)
	conn, err := db.DB.MysqlDB.DefaultGormDB()
	if err != nil {
		return
	}
	err = conn.Transaction(func(tx *gorm.DB) error {
		return model.NewUserModel().Register(conn, &userModel)
	})
	if err != nil {
		c.JSON(http.StatusOK, constant.ErrInfo{Code: constant.LogicError, Msg: err.Error()})
		return
	}
	c.JSON(http.StatusOK, constant.OK)
}

type ParamsEdit struct {
	Username string `form:"username" json:"username"`
	Phone    string `form:"phone" json:"phone" `
	Avatar   string `form:"avatar" json:"avatar"`
	Sex      int8   `form:"sex" json:"sex"`     //1 男 2 女 3未知
	State    string `form:"state" json:"state"` //1 正常 2 恶意操作禁用 3 注销
}

// @Summary	资料修改
// @Schemes
// @Description	资料修改 /api/user/edit
// @Tags			用户模块-公共
// @Accept			json
// @Param			Authorization	header		string				true	"签名"
// @Param			payload			body		ParamsEdit			true	"body"
// @success		200				{object}	constant.ErrInfo	"desc"
// @Router			/api/user/edit [post]
func Edit(c *gin.Context) {
	params := ParamsEdit{}
	if err := codec.GinBind(c, &params); err != nil {
		c.JSON(http.StatusOK, constant.ErrInfo{Code: constant.ParamsError, Msg: err.Error()})
		return
	}
	params.Username = strings.TrimSpace(params.Username)
	if params.Phone != "" && c.GetString("phone") != params.Phone {
		if ok := validation.MobileValidator(params.Phone); !ok {
			c.JSON(http.StatusOK, constant.ErrInfo{Code: constant.LogicError, Msg: "手机号格式不正确"})
			return
		}
		user, err := model.FindOne[model.UserModel](map[string]interface{}{"phone": params.Phone})
		if user.Id > 0 {
			log.NewError(c.GetString("requestId"), "registered error", err)
			c.JSON(http.StatusOK, gin.H{"code": constant.LogicError, "msg": "手机号已被占用"})
			return
		}
	}
	column := make(map[string]interface{})
	if params.Username != "" {
		column["username"] = params.Username
	}
	if params.Phone != "" {
		column["phone"] = params.Phone
	}
	if params.Avatar != "" {
		column["avatar"] = params.Avatar
	}
	if params.Sex != 0 {
		column["sex"] = params.Sex
	}
	if params.State != "" {
		column["state"] = params.State
	}

	err := model.Update(model.NewUserModel().TableName(), map[string]interface{}{"id": c.GetInt64("uid")}, column)
	if err == nil {
		c.JSON(http.StatusOK, constant.OK)
		return
	}
	log.NewError(c.GetString("requestId"), err)
	c.JSON(http.StatusOK, constant.ErrInfo{Code: constant.LogicError, Msg: "发生错误"})
}

type EditpasswordReq struct {
	Phone string `json:"phone" validate:"required"` // 手机号
	// Password string `json:"password" validate:"required"` //密码
	Password string `json:"password" validate:"required|maxLen:40|minLen:6|alphaDash" message:"required:密码不能为空|maxLen:密码最多不能超过40个字符|minLen:密码最少6个字符|alphaDash:密码仅支持字母和数字，下划线'_'及破折号'-'" label:"密码" filter:"trim"`
	Code     string `json:"code" validate:"required"` //验证码
}

// @Summary	修改密码
// @Schemes
// @Description	修改密码 /api/user/password
// @Tags			用户模块-公共
// @Accept			mpfd
// @Param			Authorization	header		string				true	"签名"
// @Param			payload			body		EditpasswordReq		true	"body"
// @success		200				{object}	constant.ErrInfo	"desc"
// @Router			/api/user/password [post]
func Editpassword(c *gin.Context) {
	params := EditpasswordReq{}
	if err := codec.GinBind(c, &params); err != nil {
		c.JSON(http.StatusOK, constant.ErrInfo{Code: constant.ParamsError, Msg: err.Error()})
		return
	}
	if ok := validation.MobileValidator(params.Phone); !ok {
		c.JSON(http.StatusOK, constant.ErrInfo{Code: constant.LogicError, Msg: "手机号格式不正确"})
		return
	}
	//验证当前账号绑定的手机号
	if c.GetString("phone") != params.Phone {
		c.JSON(http.StatusOK, constant.ErrInfo{Code: constant.LogicError, Msg: "请使用当前账号绑定的手机号"})
		return
	}
	if params.Code != "" && len(params.Code) == 6 {
		//手机验证码登录
		err := model.NewSmsLogModel().VerifySmsCode(params.Phone, params.Code, constant.VerificationCodeForReset)
		if err != nil {
			c.JSON(http.StatusOK, constant.ErrInfo{Code: constant.LogicError, Msg: err.Error()})
			return
		}
	} else {
		c.JSON(http.StatusOK, constant.ErrInfo{Code: constant.LogicError, Msg: "验证码输入错误"})
	}
	params.Password = strings.TrimSpace(params.Password)
	params.Password = funcs.EncryptLoginPass(params.Password, c.GetString("pass_salt"))
	err := model.Update(model.NewUserModel().TableName(), map[string]interface{}{"id": c.GetInt64("uid")}, map[string]interface{}{
		"password": params.Password,
	})
	if err == nil {
		c.JSON(http.StatusOK, constant.OK)
		return
	}
	c.JSON(http.StatusOK, constant.ErrInfo{Code: constant.LogicError, Msg: "发生错误"})
}

// @Summary	获取个人资料
// @Schemes
// @Description	/api/user/profile
// @Tags			用户模块-公共
// @Accept			mpfd
// @Param			Authorization	header		string											true	"签名"
// @success		200				{object}	response.JSONResult{data=model.UserLoginReply}	"desc"
// @Router			/api/user/profile [post]
func Profile(c *gin.Context) {
	user, _ := model.FindOne[model.UserModel](map[string]interface{}{"id": c.GetInt64("uid")})
	if user.Id == 0 {
		c.JSON(http.StatusOK, constant.Failed)
		return
	}
	res := model.UserBaseResponse{}
	fmt.Println(user)
	funcs.CopyStructFields(&res, user)
	c.JSON(http.StatusOK, response.JSONResult{Data: res})
}
